package com.tegtech.starter.security.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.tegtech.starter.security.encoder.NoPasswordEncoder;
import com.tegtech.starter.security.filter.JwtAuthenticationTokenFilter;
import com.tegtech.starter.security.handler.OpenAccessDeniedHandler;
import com.tegtech.starter.security.handler.OpenAuthenticationEntryPoint;
import com.tegtech.starter.security.handler.OpenLogoutSuccessHandler;
import com.tegtech.starter.security.service.SelfUserDetailsService;

/**
 * spring security配置
 * 
 * @Author Jun
 * @date 2020年5月30日 下午11:30:59
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	/** 未登陆 **/
	@Autowired
	private OpenAuthenticationEntryPoint authenticationEntryPoint;

	/** 注销成功 **/
	@Autowired
	private OpenLogoutSuccessHandler logoutSuccessHandler;

	/** 无权访问 **/
	@Autowired
	private OpenAccessDeniedHandler accessDeniedHandler;

	/** 自定义用户认证逻辑 **/
	@Autowired
	private SelfUserDetailsService userDetailsService;

	/** JWT拦截器 **/
	@Autowired
	private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
	
	// 允许匿名访问的uri，多个用逗号隔开
	@Value("${token.excludeUris}")
	private String excludeUris;

	/**
	 * 解决 无法直接注入 AuthenticationManager
	 *
	 * @return
	 * @throws Exception
	 */
	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// 这里使用自定义的加密方式(不使用加密)，security提供了 BCryptPasswordEncoder 加密可自定义
		auth.userDetailsService(userDetailsService).passwordEncoder(new NoPasswordEncoder());
	}

	/**
	 * anyRequest 			| 匹配所有请求路径 
	 * access 				| SpringEl表达式结果为true时可以访问 
	 * anonymous 			| 匿名可以访问
	 * denyAll 				| 用户不能访问 
	 * fullyAuthenticated 	| 用户完全认证可以访问（非remember-me下自动登录）
	 * hasAnyAuthority 		| 如果有参数，参数表示权限，则其中任何一个权限可以访问 
	 * hasAnyRole 			| 如果有参数，参数表示角色，则其中任何一个角色可以访问 
	 * hasAuthority 		| 如果有参数，参数表示权限，则其权限可以访问 
	 * hasIpAddress			| 如果有参数，参数表示IP地址，如果用户IP和参数匹配，则可以访问 
	 * hasRole 				| 如果有参数，参数表示角色，则其角色可以访问 
	 * permitAll			| 用户可以任意访问 
	 * rememberMe 			| 允许通过remember-me登录的用户访问 
	 * authenticated 		| 用户登录后可访问
	 */
	@Override
	protected void configure(HttpSecurity httpSecurity) throws Exception {

		httpSecurity
				// CRSF禁用，因为不使用session
				.csrf().disable()
				// 基于token，所以不需要session
				.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
				// 未登录处理类
				.and().httpBasic().authenticationEntryPoint(authenticationEntryPoint)
				// 允许匿名访问
				.and().authorizeRequests().antMatchers(excludeUris.split(",")).anonymous()
				// 除上面外的所有请求全部需要鉴权认证
				.anyRequest().authenticated().and().headers().frameOptions().disable();

		// 注销登录
		httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
		// 无权访问
		httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
		// JWT Filter
		httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
	}

	@Bean
	GrantedAuthorityDefaults grantedAuthorityDefaults() {
		return new GrantedAuthorityDefaults("");// remove the ROLE_ prefix
	}

}